/**
 * \file Expression.h
 * \brief Headers for expression-oriented Code subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __EXPRESSION_H_INCLUDED
#define __EXPRESSION_H_INCLUDED

#include <iostream>

#include "CStr.h"
#include "Type.h"
#include "Declaration.h"
#include "Base.h"

#define UnaryOp(c)\
class c : public Expression\
{\
  public:\
    c() : Expression(), data(NULL) {}\
    c(Type *_type) : Expression(_type), data(NULL) {}\
    c(Type *_type, Expression *_data) :\
      Expression(_type), data(_data) {}\
\
    ~c()\
    {\
      delete data;\
    }\
\
    c *copy()\
    {\
      return new c(type, data->copy());\
    }\
\
    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);\
    void residualize(SymbolTable *symbols)\
    {\
      data->residualize(symbols);\
    }\
    std::string toString(int indentation=0);\
    std::string toTerm();\
\
    c *replace(Code *src, Code *rplc,\
			  SymbolTable *symbols, Compound *out)\
    {\
      return new c(type, data->replace(src, rplc, symbols, out));\
    }\
\
    bool hasSideEffects() { return data->hasSideEffects(); }\
  public:\
    Expression *data;\
}

// *** DOES NOT WORK WITH CHECK(a++, a++) / a unknown. INTRODUCE NEW VARIABLES ***
#define UpdatingUnaryOp(c)\
class c : public Expression\
{\
  public:\
    c() : Expression(), data(NULL) {}\
    c(Type *_type) : Expression(_type), data(NULL) {}\
    c(Type *_type, LeftExpression *_data) :\
      Expression(_type), data(_data) {}\
\
    ~c()\
    {\
      delete data;\
    }\
\
    c *copy()\
    {\
      return new c(type, data->copy());\
    }\
\
    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);\
    void residualize(SymbolTable *symbols)\
    {\
      data->residualize(symbols);\
    }\
    std::string toString(int indentation=0);\
    std::string toTerm();\
\
    c *replace(Code *src, Code *rplc,\
			  SymbolTable *symbols, Compound *out)\
    {\
      Expression *res = data->replace(src, rplc, symbols, out);\
      if (dynamic_cast<LeftExpression*>(res))\
        return new c(type, dynamic_cast<LeftExpression*>(res));\
      else\
      {\
        delete res;\
        \
        plog.warning() << HDR << "cannot cast replacement to leftexpression" << std::endl;\
        \
        return this->copy();\
      }\
    }\
\
    bool hasSideEffects() { return data->hasSideEffects(); }\
\
  public:\
    LeftExpression *data;\
}

/** \brief Address reference.
 *
 * Reduces to the address of a LeftExpression. Note that in PEPCI, these are not
 * values that can be cast as integers, so the result of an Address can only be used
 * for assigning or dereferencing.
 */
class Address : public Expression
{
  public:
    Address() : Expression(), data(NULL) {}
    Address(Type *_type) : Expression(_type), data(NULL) {}
    Address(Type *_type, LeftExpression *_data) :
      Expression(_type), data(_data) {}

    ~Address()
    {
      delete data;
    }

    Address *copy()
    {
      return new Address(type, data->copy());
    }

    Value* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

    Address *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      Expression *res = data->replace(src, rplc, symbols, out);
      if (dynamic_cast<LeftExpression*>(res))
        return new Address(type, dynamic_cast<LeftExpression*>(res));
      else
      {
        delete res;
        
        plog.warning() << HDR << "cannot cast replacement to leftexpression" << std::endl;
        
        return this->copy();
      }
    }

    bool hasSideEffects() { return data->hasSideEffects(); }

  public:
    LeftExpression *data; ///< LeftExpression to take address of.
};

/** \brief Casting an Expression to a different Type.
 * 
 * Reduces to the same Expression, but with a different Type. In most cases, this
 * will result in an unknown value, because type conversion is only supported between
 * integers and floats.
 */
class TypeCast : public Expression
{
  public:
    TypeCast() : Expression(), tname(NULL), data(NULL) {}
    TypeCast(Type *_type, TypeName *_tname=NULL, Expression *_data=NULL) :
      Expression(_type), tname(_tname), data(_data) {}

    ~TypeCast()
    {
      delete tname;
      delete data;
    }

    TypeCast *copy()
    {
      return new TypeCast(type, tname->copy(), data->copy());
    }

    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

    TypeCast *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new TypeCast(type, tname->copy(), data->replace(src, rplc, symbols, out));
    }

    bool hasSideEffects() { return data->hasSideEffects(); }

  public:
    TypeName *tname;  ///< Name of Type to cast to.
    Expression *data; ///< Expression to cast.
};

/** \brief Getting the storage size of an expression.
 * 
 * Reduces to an IntegerValue. Array types are treated as pointers
 */
class SizeofExp : public Expression
{
  public:
    SizeofExp() : Expression(), data(NULL) {}
    SizeofExp(Type *_type, Expression *_data=NULL) : Expression(_type), data(_data) {}

    ~SizeofExp()
    {
      delete data;
    }

    SizeofExp *copy()
    {
      return new SizeofExp(type, data->copy());
    }

    IntegerValue* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

    bool hasSideEffects() { return data->hasSideEffects(); }

    SizeofExp *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new SizeofExp(type, data->replace(src, rplc, symbols, out));
    }

  public:
    Expression *data; ///< Name of expression to get size of.
};

/** \brief Getting the storage size of a type.
 * 
 * Reduces to an IntegerValue. Array types are treated as pointers
 */
class SizeofType : public Expression
{
  public:
    SizeofType() : Expression(), data(NULL) {}
    SizeofType(Type *_type, TypeName *_data=NULL) : Expression(_type), data(_data) {}

    ~SizeofType()
    {
      delete data;
    }

    SizeofType *copy()
    {
      return new SizeofType(type, data->copy());
    }

    IntegerValue* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

    bool hasSideEffects() { return false; }

    SizeofType *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      Code *res = data->replace(src, rplc, symbols, out);
      if (dynamic_cast<TypeName*>(res))
        return new SizeofType(type, dynamic_cast<TypeName*>(res));
      else
      {
        delete res;
        
        plog.warning() << HDR << "cannot cast replacement to typename" << std::endl;
        
        return this->copy();
      }
    }

  public:
    TypeName *data; ///< Name of type to get size of.
};

UnaryOp(Positive);
UnaryOp(Negative);
UnaryOp(Negation);
UnaryOp(Tilde);
UpdatingUnaryOp(PreIncr);
UpdatingUnaryOp(PreDecr);
UpdatingUnaryOp(PostIncr);
UpdatingUnaryOp(PostDecr);

/** \brief Expression with two operands.
 *
 * BinaryOps are a class of operations that work mostly the same except for the
 * calculation parts. As such, BinaryOp calls a number of protected functions to
 * do the actual processing.
 */
class BinaryOp : public Expression
{
  public:
    BinaryOp() : Expression(), left(NULL), right(NULL) {}
    BinaryOp(Type *_type) : Expression(_type), left(NULL), right(NULL) {}
    BinaryOp(Type *_type, Expression *_left, Expression *_right) :
      Expression(_type), left(_left), right(_right) {}

    ~BinaryOp()
    {
      delete left;
      delete right;
    }

    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);
    std::string toString(int indentation=0);
    std::string toTerm();

    bool hasSideEffects() { return left->hasSideEffects()||right->hasSideEffects(); }

    BinaryOp *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return construct(type, left->replace(src, rplc, symbols, out),
		       right->replace(src, rplc, symbols, out));
    }

  protected:
    /** \brief Construct a new BinaryOp of the appropriate type.
     *
     * A new BinaryOp may need to be constructed in \c reduce. An implementation of this
     * pure virtual function will do it.
     *
     * \param _type Type of the constructed operation.
     * \param _left Left hand side.
     * \param _right Right hand side.
     * \returns New BinaryOp.
     */
    virtual BinaryOp* construct(Type *_type, Expression *_left, Expression *_right) = 0;

    /** \brief Calculate the operation for two integers.
     *
     * \param a Left hand side.
     * \param b Right hand side.
     * \returns Calculated value.
     */
    virtual int calculate(int a, int b) = 0;

    /** \brief Calculate the operation for two doubles.
     *
     * \param a Left hand side.
     * \param b Right hand side.
     * \returns Calculated value.
     */
    virtual double calculate(double a, double b) = 0;

    /** \brief Return the textual representation of the operation.
     *
     * \returns String containting the representation.
     */
    virtual std::string sign(void) = 0;

    /** \brief Return the ATerm function representing the operation.
     *
     * \returns String containting the representation.
     */
    virtual std::string term(void) = 0;

  public:
    Expression *left,  ///< Left hand side.
               *right; ///< Right hand side.
};

class Comma : public Expression
{
  public:
    Comma() : Expression(), left(NULL), right(NULL) {}
    Comma(Type *_type) : Expression(_type), left(NULL), right(NULL) {}
    Comma(Type *_type, Expression *_left, Expression *_right) :
      Expression(_type), left(_left), right(_right) {}

    ~Comma()
    {
      delete left;
      delete right;
    }

    Comma *copy()
    {
      return new Comma(type, left->copy(), right->copy());
    }

    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);
    std::string toString(int indentation=0);
    std::string toTerm();

    bool hasSideEffects() { return left->hasSideEffects()||right->hasSideEffects(); }

    Comma *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new Comma(type, left->replace(src, rplc, symbols, out),
		       right->replace(src, rplc, symbols, out));
    }

  public:
    Expression *left,  ///< Left hand side.
               *right; ///< Right hand side.
};

/** \brief %Type of assignment operator.
 *
 * 
 */
enum AssignOp {AssignEq, AssignMul, AssignDiv, AssignMod, AssignPlus, AssignMin, AssignSL, AssignSR, AssignAnd, AssignExOr, AssignOr};

/** \brief Assignment.
 * 
 * Although there are different types of assignment, there is only one Assign class, with
 * an AssignOp to distinguish them.
 * 
 * Assignments can be tentative, and are referenced by Values. This means the reduction
 * of an assignment always results in a (tentative) output in \c Out.
 */
class Assign : public Expression
{
  public:
    Assign() : Expression(), op(AssignEq), left(NULL), right(NULL) {}
    Assign(Type *_type) : Expression(_type), op(AssignEq), left(NULL), right(NULL) {}
    Assign(Type *_type, AssignOp _op, LeftExpression* _left, Expression* _right) :
      Expression(_type), op(_op), left(_left), right(_right) {}

    ~Assign()
    {
      delete left;
      delete right;
    }

    Assign *copy()
    {
      return new Assign(type, op, left->copy(), right->copy());
    }

    Expression *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

    Assign *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      Expression *res = left->replace(src, rplc, symbols, out);
      if (dynamic_cast<LeftExpression*>(res))
        return new Assign(type, op, dynamic_cast<LeftExpression*>(res), right->replace(src, rplc, symbols, out));
      else
      {
        delete res;
        
        plog.warning() << HDR << "cannot cast replacement to leftexpression" << std::endl;
        
        return new Assign(type, op, left->copy(), right->replace(src, rplc, symbols, out));
      }
    }

    bool hasSideEffects() { return true; }

  public:
    AssignOp op;          ///< %Type of assignment.
    LeftExpression *left; ///< Left hand side.
    Expression *right;    ///< Right hand side.
};

/** \brief Function call.
 * 
 * FunCall not only represents function calls, but also strategy calls and external
 * strategy calls.
 */
class FunCall : public Expression
{
  public:
    typedef std::vector<Expression*>::iterator AI;

    FunCall() : Expression(), id(NULL) {}
    FunCall(Type *_type) : Expression(_type), id(NULL) {}
    FunCall(Type *_type, Code *_id) : Expression(_type), id(_id) {}
 
    ~FunCall()
    {
      delete id;
      for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
	delete (*ii);
    }

    FunCall *copy()
    {
      FunCall *res = new FunCall(type, id->copy());

      for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
	res->push_back((*ii)->copy());
	
      return res;
    }

    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

    FunCall *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      FunCall *res;

      if (dynamic_cast<Id*>(id))
	res = new FunCall(type, id->copy());
      else
	res = new FunCall(type, id->replace(src, rplc, symbols, out));

      for (AI ii=arguments.begin(); ii != arguments.end(); ++ii)
	res->push_back((*ii)->replace(src, rplc, symbols, out));
	
      return res;
    }

    /** \brief Add an argument.
     * 
     * \param code Argument Expression.
     */
    void push_back(Expression *code);

    bool hasSideEffects() { return true; }

  private:
    Expression* reduceStratego(std::string name, SymbolTable *symbols, Compound *out, Use use=NotUsed);
    Expression* reduceStrategy(std::string name, SymbolTable *symbols, Compound *out, Use use=NotUsed);
    Expression* reduceFunction(std::string name, SymbolTable *symbols, Compound *out, Use use=NotUsed);
    Expression* reduceBuiltin(std::string name, SymbolTable *symbols, Compound *out, Use use=NotUsed);

  public:
    Code *id;                           ///< Name of the function.
    std::vector<Expression*> arguments; ///< Argument vector.
};

/** \brief Conditional expression.
 */
class IfExp : public Expression
{
  public:
    IfExp() : Expression() {}
    IfExp(Type *_type) : Expression(_type) {}
    IfExp(Type *_type, Expression *_condition, Expression *_truedata, Expression *_falsedata) : Expression(_type), condition(_condition), truedata(_truedata), falsedata(_falsedata) {}
 
    ~IfExp()
    {
      delete condition;
      delete truedata;
      delete falsedata;
    }

    IfExp *copy()
    {
      return new IfExp(type, condition->copy(), truedata->copy(), falsedata->copy());
    }

    Expression* reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);
    void residualize(SymbolTable *symbols);

    std::string toString(int indentation=0);
    std::string toTerm();

    IfExp *replace(Code *src, Code *rplc,
			  SymbolTable *symbols, Compound *out)
    {
      return new IfExp(type, condition->replace(src, rplc, symbols, out),
		       truedata->replace(src, rplc, symbols, out), falsedata->replace(src, rplc, symbols, out));
    }

    bool hasSideEffects() { return condition->hasSideEffects()||
			      truedata->hasSideEffects()||falsedata->hasSideEffects(); }

  public:
    Expression *condition;
    Expression *truedata, *falsedata;    
};

#endif /* __EXPRESSION_H_INCLUDED */
